package org.xm.sk.service.data.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xm.sk.dao.MeteRepository;
import org.xm.sk.vo.Base;
import org.xm.sk.vo.ob.MarkAv;
import org.xm.sk.vo.ob.PageConf;
import org.xm.sk.vo.tb.Boder;
import org.xm.sk.vo.tb.Hairdresser;
import org.xm.sk.vo.tb.Mete;
import org.xm.sk.vo.tb.Shop;
import org.xm.sk.vo.tb.User;

@Service
@Transactional
public class MeteServiceImpl extends BaseServiceImpl<Mete> {

    private static final String CACHE_KEY = "'mete'";
    private static final String sk_CACHE_NAME = "metes";

    @Autowired
    private MeteRepository meteRepository;

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Mete findById(int id) {
        return this.meteRepository.getOne(id);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Set<Mete> findByName(String name) {
        return this.meteRepository.findByName(name);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_all'")
    public List<Mete> findAll() {
        return this.meteRepository.findAll();
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public List<Mete> findAll(Mete mete) {
        ExampleMatcher matcher = ExampleMatcher.matching();
        Example<Mete> ex = Example.of(mete, matcher);
        return this.meteRepository.findAll(ex);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Object findAllByPage(PageConf pageConf) {
        List<Order> orders = new ArrayList<Order>();
        if (null != pageConf.getSortNames()) {
            for (int i = 0; i < pageConf.getSortNames().size(); i++) {
                orders.add(new Order(pageConf.getSortOrders().get(i),
                        pageConf.getSortNames().get(i)));
            }
        } else {
            orders.add(new Order(Direction.DESC, "id"));
        }
        Sort sort = Sort.by(orders);
        Pageable pageable = null;
        Specification<Mete> querySpecifi = new Specification<Mete>() {

            private static final long serialVersionUID = -6554775700006656313L;

            @Override
            public Predicate toPredicate(Root<Mete> root,
                    CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                Predicate predicate = cb.and(
                        cb.equal(root.get("datalevel").as(Integer.class), 0));
                List<Predicate> predicates = new ArrayList<>();
                if (null != pageConf.getSearch()
                        && 0 < pageConf.getSearch().trim().length()) {
                    String search = pageConf.getSearch().trim();
                    // 模糊查找
                    predicates.add(cb.like(root.get("mark").as(String.class),
                            "%" + search + "%"));
                    Join<Mete, Shop> joinShops = root.join("shops",
                            JoinType.LEFT);
                    predicates.add(cb.like(joinShops.get("name").as(String.class),
                            "%" + search + "%"));
                    Join<Mete, Hairdresser> joinHairdressers = root.join("hairdressers",
                            JoinType.LEFT);
                    predicates.add(cb.like(joinHairdressers.get("name").as(String.class),
                            "%" + search + "%"));
                    Join<Mete, User> joinUser = root.join("user",
                            JoinType.LEFT);
                    predicates.add(cb.or(
                            cb.like(joinUser.get("name").as(String.class),
                                    "%" + search + "%"),
                            cb.like(joinUser.get("nickname")
                                    .as(String.class), "%" + search + "%"),
                            cb.like(joinUser.get("phone")
                                    .as(String.class), "%" + search + "%")));
                    predicates.add(
                            cb.like(root.get("modifydate").as(String.class),
                                    "%" + search + "%"));
                }
                if (predicates.isEmpty())
                    return predicate;
                // and到一起的话所有条件就是且关系，or就是或关系
                return cb.and(
                        cb.or(predicates
                                .toArray(new Predicate[predicates.size()])),
                        predicate);
            }
        };
        if (null != pageConf.getPage() && null != pageConf.getSize())
            pageable = PageRequest.of(pageConf.getPage(), pageConf.getSize(),
                    sort);
        if (null != pageable)
            return this.meteRepository.findAll(querySpecifi, pageable);
        else
            return this.meteRepository.findAll(querySpecifi, sort);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0+'_'+#p1")
    public Object[] findAllByPage(PageConf pageConf, Object object) {
        if (null != object && object instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) object;
            String owner = map.get("owner").toString();
            Pageable pageable = PageRequest.of(pageConf.getPage(),
                    pageConf.getSize());
            Page<Mete> metes = null;
            MarkAv markAv = null;
            if ("shop".equals(owner)) {
                Integer ownerid = Integer.valueOf(map.get("ownerid").toString());
                metes = this.meteRepository.listByShop(pageable, ownerid);
                markAv = this.meteRepository.cvByShop(ownerid);
            } else if ("hairdresser".equals(owner)) {
                if (null != map.get("ownerid")) {
                    Integer ownerid = Integer.valueOf(map.get("ownerid").toString());
                    if (null == map.get("mark")) {
                        metes = this.meteRepository.listByHairdressers(pageable,
                                ownerid);
                    } else {
                        int mark = (int) map.get("mark");
                        metes = this.meteRepository
                                .listByMark4Hairdressers(pageable, ownerid, mark);
                    }
                    markAv = this.meteRepository.cvByHairdressers(ownerid);
                } else {
                    metes = this.meteRepository.listAllMete4Hairdresser(pageable);
                }
            } else if ("order".equals(owner)) {
                Integer ownerid = Integer.valueOf(map.get("ownerid").toString());
                Boder boder = new Boder();
                boder.setId(ownerid);
                metes = this.meteRepository.findByBoder(pageable, boder);
                MarkAv MarkAv1 = null;
                MarkAv MarkAv2 = null;
                if (null != metes && 0 < metes.getContent().size()) {
                    for (Mete mete : metes.getContent()) {
                        if (Base.isValid(mete)) {
                            if (null == MarkAv1)
                                for (Shop shop : mete.getShops()) {
                                    MarkAv1 = this.meteRepository.cvByShop(shop.getId());
                                    break;
                                }
                            if (null == MarkAv2)
                                for (Hairdresser hairdresser : mete.getHairdressers()) {
                                    MarkAv2 = this.meteRepository.cvByHairdressers(hairdresser.getId());
                                    break;
                                }
                        }
                    }
                }
                return new Object[] { metes, MarkAv1, MarkAv2 };
            } else {
                return new Object[] { findAllByPage(pageConf), null };
            }
            return new Object[] { metes, markAv };
        } else {
            return new Object[] { findAllByPage(pageConf), null };
        }
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    // @CachePut(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0.id")
    public Mete update(Mete mete) {
        mete.setEditer(user().getUserid());
        mete.setEditerName(user().getUsername());
        mete.setModifydate(new Date());
        return this.meteRepository.save(mete);
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(int id) {
        this.meteRepository.delete(id, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(Set<Integer> ids) {
        this.meteRepository.delete(ids, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public Mete add(Mete mete) {
        mete.setCreator(user().getUserid());
        mete.setEditer(user().getUserid());
        mete.setCreatorName(user().getUsername());
        mete.setEditerName(user().getUsername());
        return this.meteRepository.saveAndFlush(mete);
    }

}
